package com.oliver.proxy.midjourney.application.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.oliver.proxy.midjourney.ProxyProperties;
import com.oliver.proxy.midjourney.domain.entity.FinalProdPic;
import com.oliver.proxy.midjourney.infrastructure.mapper.FinalProdPicMapper;
import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Slf4j
@Service
public class LinkProcessingService {

    private static final String BASE_DIR = System.getProperty("user.home") + File.separator + "pic";
    private static final String SOURCE_PATH = BASE_DIR + File.separator + "processing" + File.separator + "op";
    private static final String TARGET_PATH = BASE_DIR + File.separator + "finishedOp";
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final FinalProdPicMapper finalProdPicMapper;
    private final MinioClient minioClient;
    private final ProxyProperties properties;
    private ExecutorService executorService = Executors.newSingleThreadExecutor();
    private ExecutorService uploadExecutorService = Executors.newFixedThreadPool(10);
    private final AtomicBoolean isProcessing = new AtomicBoolean(false);

    @Autowired
    public LinkProcessingService(FinalProdPicMapper finalProdPicMapper, MinioClient minioClient, ProxyProperties properties) {
        this.finalProdPicMapper = finalProdPicMapper;
        this.minioClient = minioClient;
        this.properties = properties;
    }

    @PostConstruct
    public void init() {
        createDirectories();
        setupMinioClient();
        executorService = Executors.newSingleThreadExecutor();
        uploadExecutorService = Executors.newFixedThreadPool(10);
        log.info("LinkProcessingService initialized successfully");
    }

    private void createDirectories() {
        try {
            Files.createDirectories(Paths.get(SOURCE_PATH));
            Files.createDirectories(Paths.get(TARGET_PATH));
            log.info("Successfully created directories: SOURCE_PATH={}, TARGET_PATH={}", SOURCE_PATH, TARGET_PATH);
        } catch (IOException e) {
            log.error("Failed to create directories", e);
            throw new RuntimeException("Failed to create required directories", e);
        }
    }

    private void setupMinioClient() {
        try {
            // Create bucket if it doesn't exist
            boolean bucketExists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(properties.getMinio().getBucketName())
                            .build()
            );
            
            if (!bucketExists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(properties.getMinio().getBucketName())
                                .build()
                );
                log.info("Created MinIO bucket: {}", properties.getMinio().getBucketName());
            }
            
            // 设置存储桶策略为公共读取，确保直接URL可以访问
            String publicPolicy = """
                {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": "*",
                            "Action": ["s3:GetObject"],
                            "Resource": ["arn:aws:s3:::%s/*"]
                        }
                    ]
                }
                """.formatted(properties.getMinio().getBucketName());
            
            // 应用存储桶策略
            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(properties.getMinio().getBucketName())
                            .config(publicPolicy)
                            .build()
            );
            
            log.info("Set public read policy for bucket: {}", properties.getMinio().getBucketName());
        } catch (Exception e) {
            log.error("Failed to setup MinIO client", e);
            throw new RuntimeException("Failed to setup MinIO", e);
        }
    }

    @Scheduled(fixedRate = 5000)
    public void processDirectory() {
        if (isProcessing.get()) {
            log.debug("Previous processing still in progress, skipping this iteration");
            return;
        }

        Path sourcePath = Paths.get(SOURCE_PATH);
        if (!Files.exists(sourcePath)) {
            log.error("Source directory does not exist: {}", SOURCE_PATH);
            createDirectories();
            return;
        }

        isProcessing.set(true);
        CompletableFuture.runAsync(this::processDirectoryInBackground, executorService);
    }

    private void processDirectoryInBackground() {
        log.debug("Starting directory processing task");
        try {
            List<Path> files = Files.walk(Paths.get(SOURCE_PATH))
                    .filter(Files::isRegularFile)
                    .filter(path -> path.toString().endsWith(".jpg") ||
                            path.toString().endsWith(".png") ||
                            path.toString().endsWith(".jpeg") ||
                            path.toString().endsWith(".gif"))
                    .collect(Collectors.toList());

            Thread.sleep(3000);
            if (files.isEmpty()) {
                log.debug("No files found to process");
                return;
            }

            log.info("Found {} files to process", files.size());

            // Create a list to hold all futures
            List<CompletableFuture<Void>> uploadFutures = files.stream()
                .map(file -> CompletableFuture.runAsync(() -> processNewFile(file), uploadExecutorService))
                .collect(Collectors.toList());

            // Wait for all uploads to complete
            CompletableFuture.allOf(uploadFutures.toArray(new CompletableFuture[0])).join();

            log.info("All {} files have been processed", files.size());
        } catch (IOException e) {
            log.error("Error processing directory", e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            isProcessing.set(false);
        }
    }

    protected void processNewFile(Path sourceFile) {
        try {
            // Get relative path from source root
            Path relativePath = Paths.get(SOURCE_PATH).relativize(sourceFile);
            Path targetFile = Paths.get(TARGET_PATH, relativePath.toString());

            // Create target directory if it doesn't exist
            Files.createDirectories(targetFile.getParent());

            // Move the file
            Files.move(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING);

            // Upload file and get URL
            String fileUrl = uploadFile(targetFile.toFile());
            String fileName = targetFile.getFileName().toString();
            String picType = determinePicType(fileName);
            Integer priority = null;
            if (!fileName.contains("预览图")) {
                priority = Integer.parseInt(fileName.substring(fileName.lastIndexOf("_") + 1).split("\\.")[0]);
            }
            // 提取货号（文件名第一部分）
            String productCode = fileName.split("_")[0];

            // 创建并设置FinalProdPic对象
            FinalProdPic finalProdPic = new FinalProdPic();
            finalProdPic.setPicPriority(priority);
            finalProdPic.setPicUrl(fileUrl);
            finalProdPic.setAttritube1(targetFile.toString());
            finalProdPic.setAttritube2(productCode);
            finalProdPic.setPicType(picType);

            // 使用MyBatis插入数据
            finalProdPicMapper.insertProdPic(finalProdPic);

            log.info("Processed and uploaded file: {} -> {} (URL: {})", sourceFile, targetFile, fileUrl);

            // Check and remove empty source directories
            Path sourceDir = sourceFile.getParent();
            removeEmptyDirectories(sourceDir);

        } catch (Exception e) {
            log.error("Error processing file: " + sourceFile, e);
        }
    }

    private void removeEmptyDirectories(Path directory) {
        try {
            // 确保目录在SOURCE_PATH下
            if (!directory.toString().startsWith(SOURCE_PATH)) {
                return;
            }

            // 检查目录是否为空
            if (Files.isDirectory(directory) && Files.list(directory).count() == 0) {
                Files.delete(directory);
                log.info("Removed empty directory: {}", directory);

                // 递归检查父目录
                Path parent = directory.getParent();
                if (parent != null && !parent.toString().equals(SOURCE_PATH)) {
                    removeEmptyDirectories(parent);
                }
            }
        } catch (IOException e) {
            log.error("Error removing empty directory: " + directory, e);
        }
    }

    private String determinePicType(String fileName) {
        if (fileName.contains("场景图")) return "场景图";
        if (fileName.contains("尺寸图")) return "尺寸图";
        if (fileName.contains("预览图")) return "预览图";
        if (fileName.contains("详情图")) return "详情图";
        return "其他";
    }

    private String uploadFile(File file) throws Exception {
        String baseName = file.getName();
        String contentType = determineContentType(file.getName());
        
        // Upload to MinIO
        minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(properties.getMinio().getBucketName())
                        .object(baseName)
                        .filename(file.getAbsolutePath())
                        .contentType(contentType)
                        .build()
        );
        
        // 生成永久有效的直接URL（不使用预签名）
        String endpoint = properties.getMinio().getEndpoint();
        // 移除结尾斜杠（如果存在）
        if (endpoint.endsWith("/")) {
            endpoint = endpoint.substring(0, endpoint.length() - 1);
        }
        String fileUrl = endpoint + "/" + properties.getMinio().getBucketName() + "/" + baseName;
        
        log.info("Uploaded file to MinIO: {}", fileUrl);
        return fileUrl;
    }
    
    private String determineContentType(String fileName) {
        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        return switch (extension) {
            case "jpg", "jpeg" -> "image/jpeg";
            case "png" -> "image/png";
            case "gif" -> "image/gif";
            default -> "application/octet-stream";
        };
    }

    @PreDestroy
    public void shutdown() {
        executorService.shutdown();
        uploadExecutorService.shutdown();
        try {
            if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
            if (!uploadExecutorService.awaitTermination(30, TimeUnit.SECONDS)) {
                uploadExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            uploadExecutorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
        log.info("ExecutorServices shut down successfully");
    }
}
